package com.muyu.system.danmo.service.impl;

import cn.hutool.json.JSONUtil;
import com.muyu.system.danmo.domain.*;
import com.muyu.system.danmo.dto.*;
import com.muyu.common.core.enums.PayChannel;
import com.muyu.common.core.enums.PayStatus;
import com.muyu.system.danmo.enums.RefundStatus;
import com.muyu.system.danmo.converter.PayMapper;
import com.muyu.system.danmo.service.*;
import com.muyu.system.danmo.util.OrderNoGenerateUtil;
import com.muyu.system.danmo.vo.PayResultVO;
import com.muyu.system.danmo.vo.PayServiceVO;
import com.muyu.system.danmo.vo.RefundResultServiceVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.rocketmq.common.message.Message;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;

import static com.muyu.common.core.constant.TagConstant.PAY_NOTICE_TAG;
import static com.muyu.common.core.constant.TagConstant.REFUND_NOTICE_TAG;
import static com.muyu.common.core.constant.TopicConstant.COMMON_TOPIC_DELAY;
import static com.muyu.common.core.constant.TopicConstant.COMMON_TOPIC_NORMAL;
import static com.muyu.system.danmo.util.TagConstant.PAY_DELAY_TAG;
import static com.muyu.system.danmo.util.TagConstant.REFUND_DELAY_TAG;


@Service
@Slf4j
@RequiredArgsConstructor
public class PayServiceImpl implements PayService {

    private final MchInfoService mchInfoService;

    private final PayOrderService payOrderService;

    private final WechatPayConfigService wechatPayConfigService;

    private final AliPayConfigService aliPayConfigService;

    private final PayMapper payMapper;

    private final Map<String,PayChannelService> payChannelServiceMap;

    private final RocketMQTemplate rocketMQTemplate;

    private final RefundOrderService refundOrderService;


    @Override
    public PayServiceVO pay(PayServiceDTO payServiceDTO) {
        // 保存订单信息
        PayOrder payOrder = saveOrder(payServiceDTO);
        // 调用支付接口
        PayChannelService payChannelService = payChannelServiceMap.get(payOrder.getPayChannel().getServiceName());
        PayResultVO payResult = payChannelService.pay(payServiceDTO, payOrder);
        if (payResult.getStatus() != PayStatus.PENDING){
            // 更新订单状态
            payOrder.setPayStatus(payResult.getStatus());
            payOrder.setTransactionId(payResult.getTransactionId());
            payOrder.setCompletedTime(payResult.getCompletedTime());
            payOrderService.updateById(payOrder);
        }else {
            DelayQueryPaymentInfo delayQueryPaymentInfo = new DelayQueryPaymentInfo();
            delayQueryPaymentInfo.setOrderNo(payOrder.getOrderNo());
            delayQueryPaymentInfo.setMchCode(payServiceDTO.getMchCode());
            delayQueryPaymentInfo.setPayChannel(payOrder.getPayChannel());
            delayQueryPaymentInfo.setBizOrderNo(payOrder.getBizOrderNo());
            // 如果是进行中，主动进行延迟查询，不能依赖回调
            org.springframework.messaging.Message<String> msg = MessageBuilder.withPayload(JSONUtil.toJsonStr(delayQueryPaymentInfo)).build();
            SendResult result = rocketMQTemplate.syncSend(COMMON_TOPIC_DELAY + ":" + PAY_DELAY_TAG, msg, 3000, 9);
            log.info("发送延迟消息成功！：{}",result.getMsgId());
        }
        return payMapper.to(payResult);
    }

    @Override
    public ResponseEntity.BodyBuilder payNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        PayChannelService payChannelService = payChannelServiceMap.get(payChannel.getServiceName());
        return payChannelService.payNotify(request, payChannel,mchCode);
    }

    @Override
    public ResponseEntity.BodyBuilder refundPayNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        PayChannelService payChannelService = payChannelServiceMap.get(payChannel.getServiceName());
        return payChannelService.refundPayNotify(request, payChannel,mchCode);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void completeOrder(PaymentCallBackResult paymentCallBackResult) {
        PayOrder payOrder = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, paymentCallBackResult.getOrderNo())
                .oneOpt().orElseThrow(() -> new RuntimeException("订单不存在"));
        if(payOrder.getPayStatus().equals(paymentCallBackResult.getStatus())){
            return;
        }
        if(payOrder.getPayStatus().equals(PayStatus.SUCCESS)){
            return;
        }
        payOrder.setCompletedTime(paymentCallBackResult.getCompletedTime());
        payOrder.setTransactionId(paymentCallBackResult.getTransactionId());
        payOrder.setOpenId(paymentCallBackResult.getOpenId());
        payOrder.setPayStatus(paymentCallBackResult.getStatus());
        payOrderService.updateById(payOrder);
        // 发送mq通知业务方
        SendResult sendResult = rocketMQTemplate.syncSend(COMMON_TOPIC_NORMAL + ":" + PAY_NOTICE_TAG, JSONUtil.toJsonStr(paymentCallBackResult));
        log.info("发送mq通知业务方成功1！：{}",sendResult.getMsgId());
    }

    @Override
    public RefundResultServiceVO refund(RefundServiceDTO refundDTO) {
        // 保存退款订单信息
        RefundOrder refundOrder = refundSave(refundDTO);
        // 执行退款
        PayChannelService payChannelService = payChannelServiceMap.get(refundOrder.getPayChannel().getServiceName());
        RefundResultServiceVO refund = payChannelService.refund(refundDTO, refundOrder);
        refund.setPayChannel(refundOrder.getPayChannel());
        if(!refund.getStatus().equals(PayStatus.PENDING)){
            // 更新订单的状态信息
            refundOrder.setRefundStatus(refund.getStatus());
            refundOrder.setCompletedDate(refund.getCompletedTime());
            refundOrder.setRefundTransactionId(refund.getTransactionId());
            refundOrderService.updateById(refundOrder);
            if(refund.getStatus().equals(PayStatus.SUCCESS)){
                // 修改支付订单的退款状态
                payOrderService.lambdaUpdate().eq(PayOrder::getOrderNo, refundDTO.getOrderNo())
                        .set(PayOrder::getRefundStatus, RefundStatus.ALL)
                        .update();
            }
        }else {
            org.springframework.messaging.Message<String> msg = MessageBuilder.withPayload(JSONUtil.toJsonStr(refundOrder)).build();
            SendResult result = rocketMQTemplate.syncSend(COMMON_TOPIC_DELAY + ":" + REFUND_DELAY_TAG, msg, 3000, 9);
            log.info("发送退款延迟消息成功！：{}",result.getMsgId());
        }
        return refund;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void completeRefundOrder(RefundResultServiceVO refundResultServiceVO) {
        RefundOrder refundOrder = refundOrderService.lambdaQuery().eq(RefundOrder::getRefundOrderNo, refundResultServiceVO.getRefundOrderNo())
                .oneOpt().orElseThrow(() -> new RuntimeException("退款订单不存在"));
        PayOrder payOrder = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, refundOrder.getOrderNo())
                .oneOpt().orElseThrow(() -> new RuntimeException("订单不存在"));
        if(refundOrder.getRefundStatus().equals(refundResultServiceVO.getStatus())){
            return;
        }
        if(refundOrder.getRefundStatus().equals(PayStatus.SUCCESS)){
            return;
        }
        refundOrder.setRefundTransactionId(refundResultServiceVO.getTransactionId());
        refundOrder.setRefundStatus(refundResultServiceVO.getStatus());
        refundOrder.setCompletedDate(refundResultServiceVO.getCompletedTime());
        refundOrderService.updateById(refundOrder);
        if(refundOrder.getRefundStatus().equals(PayStatus.SUCCESS)){
            payOrder.setRefundStatus(RefundStatus.ALL);
        }
        payOrderService.updateById(payOrder);
        // 发送mq通知业务方
        SendResult sendResult = rocketMQTemplate.syncSend(COMMON_TOPIC_NORMAL + ":" + REFUND_NOTICE_TAG, JSONUtil.toJsonStr(refundResultServiceVO));
        log.info("退款发送mq通知业务方成功！：{}",sendResult.getMsgId());
    }

    @Override
    public void close(CloseDTO closeDTO) {
        PayOrder payOrder = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, closeDTO.getOrderNo())
                .oneOpt().orElseThrow(() -> new RuntimeException("订单不存在"));
        if(!payOrder.getPayStatus().equals(PayStatus.PENDING)){
            throw new RuntimeException("订单必须处于支付中！");
        }
        // 执行关闭订单
        PayChannelService payChannelService = payChannelServiceMap.get(payOrder.getPayChannel().getServiceName());
        payChannelService.close(payOrder);
        // 更新订单状态
        payOrder.setPayStatus(PayStatus.TIMEOUT);
        payOrderService.updateById(payOrder);
    }


    /**
     * 保存退款订单
     * @param refundDTO
     * @return
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public RefundOrder refundSave(RefundServiceDTO refundDTO){
        // 获取订单信息
        PayOrder payOrder = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, refundDTO.getOrderNo())
                .oneOpt().orElseThrow(() -> new RuntimeException("订单不存在"));
        if(!payOrder.getPayStatus().equals(PayStatus.SUCCESS)){
            throw new RuntimeException("支付订单信息未完成");
        }
        // 校验退款订单
        Long count = refundOrderService.lambdaQuery().eq(RefundOrder::getRefundBizOrderNo, refundDTO.getRefundBizOrderNo()).count();
        if (count > 0){
            throw new RuntimeException("退款订单号已存在");
        }
        // 保存退款订单信息
        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setMchCode(payOrder.getMchCode());
        refundOrder.setOrderId(payOrder.getId());
        refundOrder.setOrderNo(payOrder.getOrderNo());
        refundOrder.setPayChannel(payOrder.getPayChannel());
        refundOrder.setRefundAmount(payOrder.getTotalAmount());
        refundOrder.setRefundBizOrderNo(refundDTO.getRefundBizOrderNo());
        refundOrder.setRefundOrderNo(OrderNoGenerateUtil.refund());
        refundOrder.setRefundStatus(PayStatus.PENDING);
        refundOrderService.save(refundOrder);
        return refundOrder;
    }

    /**
     * 保存订单信息
     * @param payServiceDTO
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public PayOrder saveOrder(PayServiceDTO payServiceDTO){
        // 获取商户信息
        mchInfoService.lambdaQuery().eq(MchInfo::getMchCode,
                payServiceDTO.getMchCode()).oneOpt().orElseThrow(() -> new RuntimeException("商户不存在"));
        // 校验订单
        Long count = payOrderService.lambdaQuery().eq(PayOrder::getBizOrderNo, payServiceDTO.getBizOrderNo())
                .count();
        if (count > 0){
            throw new RuntimeException("订单已存在");
        }
        // 校验配置信息
        switch(payServiceDTO.getPayMethod().getPayChannel()){
            case ALI :
                aliPayConfigService.lambdaQuery()
                        .eq(AliPayConfig::getMchCode, payServiceDTO.getMchCode())
                        .oneOpt().orElseThrow(() -> new RuntimeException("支付宝配置不存在"));
                break;
            case WECHAT :
                wechatPayConfigService.lambdaQuery()
                        .eq(WechatPayConfig::getMchCode, payServiceDTO.getMchCode())
                        .oneOpt().orElseThrow(() -> new RuntimeException("微信配置不存在"));
                break;
            default:
                throw new RuntimeException("支付配置不存在");
        }
        // 生成订单号
        String orderNo = OrderNoGenerateUtil.pay();
        PayOrder payOrder = payMapper.to(payServiceDTO);
        payOrder.setOrderNo(orderNo);
        payOrder.setPayChannel(payServiceDTO.getPayMethod().getPayChannel());
        payOrder.setPayStatus(PayStatus.PENDING);
        payOrderService.save(payOrder);
        return payOrder;
    }





}
